Ritorna all'inizio


Divergenza Operazionale

Il design dei linguaggi di programmazione è fortemente influenzato dal paradigma su cui essi si basano. I linguaggi imperativi, da un lato, si fondano sul concetto di stato mutabile e di sequenza di istruzioni; i linguaggi funzionali ad alto ordine (HOFL, Higher-Order Functional Languages), dall’altro, enfatizzano funzioni come l'immutabilità e astrazione.

Questa differenza paradigmatica si riflette in modo sostanziale sulla semantica operazionale, di conseguenza condiziona profondamente il design dei compilatori.

Nei linguaggi imperativi come il C, la semantica operazionale si appoggia ad un modello di macchina sequenziale:

- esiste uno stato globale (memoria, registri, stack, ecc.)

- ogni istruzione produce una transizione di stato deterministica

- il flusso di controllo è esplicito, strutturato tramite costrutti sintattici come cicli, salti condizionali, chiamate di procedura...

La traduzione compilativa è pertanto direttamente "mappabile" all'operazionalità algoritmica intrinseca del design hardware.

Le istruzioni si trasformano in sequenza di codici macchina, con gestione dello stato affidata al runtime o alla macchina sottostante. Questo è un approccio allineato con l'architettura Von Neumann. Il risultato è un compilatore snello, relativamente facile da costruire e con una struttura tutto sommato basilare (a meno di ottimizzazioni).

Nei linguaggi funzionali di ordine superiore (HOFL) come Haskell, OCaml, Scheme ecc. la semantica operazionale diverge radicalmente:

- vi è la totale assenza di stato mutabile (o stato controllato tramite costrutti specifici, anche se in linguaggi non puri come OCaml è possibile)

- le funzioni sono valori di prima classe, possono tranquillamente essere usate come argomenti, restituite come risultati, contenute in strutture dati; non vi è fondalmentale differenza tra funzioni e tipi

- la valutazione può essere pigra (lazy) o eager, introducendo divergenze semantiche anche tra linguaggi funzionali stessi

- la nozione di ambiente di valutazione sostituisce quella di stato globale: il risultato di un'espressione dipende dall'associazione delle variabili ai valori (closure)

A differenza dell'imperativo, l'esecuzione non è una sequenza lineare di aggiornamenti di stato, bensì una riscrittura di espressioni secondo regole di inferenza legate alla semantica del linguaggio. Questo spostamento implica che il compilatore debba gestire cose come:

- closure, ovvero strutture dati che catturano un ambiente e permettono la valutazione corretta delle funzioni

- garbage collection: la memoria non è apertamente gestibile come nei linguaggi imperativi, essa è astratta e accessibile in maniera indiretta. L'implicazione principale è che la memoria venga deallocata tramite un sistema di tracciamento di oggetti non più raggiungibili

Questa divergenza semantica crea sfide non indifferenti nel design dei compilatori HOFL.

Considerazione personale: A tale ragione ritengo il paradigma funzionale molto elegante per descrivere la computazione in modo formale, ma non lo ritengo adatto a sostituire il paradigma imperativo, perché è più adatto a lavorare all'interno di esso. E non sono l'unico a crederlo: molti linguaggi di programmazione sono cosiddetti linguaggi multiparadigma (tra cui Python, Java e molti altri), che implementano tramite costrutti sintattici la possibilità di definire espressioni funzionali. Ciò è molto utile, perché esistono delle circostanze in cui l'espressività semantica di un costrutto funzionale è molto più efficace nel definire un procedimento algoritmico.

Articoli correlati